package cn.yx.application.gateway.handler;

import cn.hutool.core.util.StrUtil;
import cn.yx.application.gateway.constant.message.GatewayMessageKey;
import cn.yx.common.core.constant.MdcConstant;
import cn.yx.common.core.enums.http.HttpHeaderCustomEnum;
import cn.yx.common.core.pojo.ResponseVo;
import cn.yx.common.core.util.IdUtil;
import cn.yx.common.i18n.service.I18nMessageService;
import cn.yx.common.webflux.util.WebFluxUtil;
import cn.yx.sys.remote.dto.SysI18nMessageRemoteDto;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * <p>网关统一异常处理</p>
 *
 * @author Wgssmart
 */
@Order(-1)
@Configuration
@Slf4j
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {

    private final I18nMessageService i18nMessageService;

    public GatewayExceptionHandler(I18nMessageService i18nMessageService) {
        this.i18nMessageService = i18nMessageService;
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        if (exchange.getResponse().isCommitted()) {
            return Mono.error(ex);
        }
        String transactionId = getTransactionId(exchange);
        MDC.put(MdcConstant.TRACE_ID, transactionId);
        log.error("gateway exception, request url: {}, message: {}", exchange.getRequest().getPath(), ex.getMessage());
        log.error("gateway exception stack trace", ex);
        SysI18nMessageRemoteDto sysI18nMessageRemoteDto = i18nMessageService.getSysI18nMessage(getErrorMessage(ex));
        // TODO 根据消息level异步推送消息到指定人员
        ResponseVo<String> responseVo = ResponseVo.fail(sysI18nMessageRemoteDto.getMessage());
        responseVo.setTransId(transactionId);
        return WebFluxUtil.write(response, responseVo);
    }

    /**
     * 获取异常信息
     *
     * @param throwable
     * @return
     */
    private String getErrorMessage(Throwable throwable) {
        if (throwable instanceof NotFoundException) {
            return GatewayMessageKey.SERVICE_NOT_FOUND;
        }
        if (throwable instanceof ResponseStatusException) {
            return GatewayMessageKey.RESPONSE_STATUS_ERROR;
        }
        return GatewayMessageKey.INTERNAL_SERVER_ERROR;
    }

    /**
     * 获取transactionId，有可能找不到服务，未经过GatewayLoggerFilter，所以请求头中的transactionId可能为空，
     * 如果为空，直接生成transactionId
     *
     * @param exchange
     * @return
     */
    private String getTransactionId(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        String transactionId = request.getHeaders().getFirst(HttpHeaderCustomEnum.TRANSACTION_ID.getName());
        if (StrUtil.isNotEmpty(transactionId)) {
            return transactionId;
        }
        return IdUtil.id();
    }

}